﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace SqlCeDriver
{
	/*
		Form to display and allow searching of

		copy SourceForm2 from DbView for the drive to use.
		This is rapidly admitting that this will never move away from Winforms to WPF
		This may be useful to other drivers as well

		This created 07/2012, but the copied code is circa the stone age.
		Hence the poor naming.
	*/
	public partial class ShowSourceForm : Form
	{
		public ShowSourceForm(String itemName, String theProcSource, String searchText)
		{
			InitializeComponent();

			// unselect
			this.theSource.Text += theProcSource;
			this.theSource.SelectionLength = 0;
			// this.theSource.ReadOnly = true;
			// fix up caption
			this.Text = String.Format("Source: [{0}]", itemName);

			int lines = LineCount(theProcSource);
			if (lines > 30)  // just too many
				BtnZoom_Click(this.btnZoom, new EventArgs());

			this.toolStripStatusLines.Text = lines.ToString();
			this.toolStripStatusChars.Text = theProcSource.Length.ToString();

			this.searchText = searchText;
			if (this.searchText.Length > 0)
			{
				this.textSearch.Text = this.searchText;
				OnFind();
			}
		}
		private void BtnClose_Click(object sender, System.EventArgs e)
		{
			this.Close();
		}

		int LineCount(String text)
		{
			int pos = text.IndexOf('\n');
			int lineCount = 1;
			while (pos >= 0)
			{
				++lineCount;
				pos = text.IndexOf('\n', pos + 1);
			}
			return lineCount;
		}

		int MatchCount()
		{
			// find location of all the seek 
			String usource = theSource.Text.ToUpper();
			String useek = this.searchText.ToUpper();
			// for case of searching empty source 
			if (useek.Length > usource.Length) return 0;
			int matchCount = 0;
			int found = usource.IndexOf(useek);
			while (found > -1)
			{
				++matchCount;
				found = usource.IndexOf(useek, found + useek.Length);
			}
			return matchCount;
		}

		private void TheSource_TextChanged(object sender, System.EventArgs e)
		{
		}

		private int FindNext(String source, int fromPos)
		{
			// find location of all the seek 
			String usource = source.ToUpper();
			String useek = this.searchText.ToUpper();
			// for case of searching empty source (ie. not allowed to see the source) this should return nothing
			if (useek.Length + fromPos > usource.Length) return usource.IndexOf(useek);

			int found = usource.IndexOf(useek, fromPos);
			// loop around
			if (found == -1)
				found = usource.IndexOf(useek);
			return found;
		}

		private int FindPrev(String source, int currentSelPos)
		{
			if (currentSelPos < 0)
				return -1;
			// find location of all the seek 
			String usource = source.ToUpper();
			String useek = this.searchText.ToUpper();
			// 
			int lastPos = -1;  // nearest previous match to currenct
			while (true)
			{
				int found = usource.IndexOf(useek, lastPos + 1);
				if (found == -1 || found >= currentSelPos)
					break;
				else
					lastPos = found;
			}

			return lastPos;
		}

		private String ModifySource(String source)
		{
			// find location of all the seek 
			String usource = source.ToUpper();
			String useek = this.searchText.ToUpper();
			List<int> offsets = new List<int>();
			int startpos = 0;
			while (true)
			{
				int found = usource.IndexOf(useek, startpos);
				if (found > startpos)
				{
					offsets.Add(found);
					startpos = found + useek.Length;
				}
				else
					break;
			}
			for (int idx = 0; idx < offsets.Count; ++idx)
			{
				int element = offsets.Count - idx - 1;
				// add ending markup first
				source = source.Insert((int)offsets[element] + useek.Length, "\\cf1\\b0 ");
				// make bold and change the colour.
				source = source.Insert((int)offsets[element], "\\b\\cf2 ");
			}

			return source.Replace("\n", "\\line\n");
		}

		private void OnFind()
		{
			String source = theSource.Text;
			// if empty then set text to non-rtf.
			if (this.searchText.Length == 0)
			{
				theSource.Text = source;
			}
			else
			{
				// convert to rtf 
				// (see; http://www.pindari.com/rtf1.html or Google "rtf tutorial")
				String rtfHead =
					// header
					@"{\rtf1\ansi\deff0" + "\n" +
					// mark colour
					@"{\colortbl;\red0\green0\blue0;\red255\green0\blue0;}" + "\n" +
					// font definition
					@"{\fonttbl {\f0 Courier new;}}\fs16" + "\n";     // 16 = 8 point

				String rtfTail = @"}";  // as per rtf spec
				theSource.Rtf = rtfHead + ModifySource(source) + rtfTail;
				OnNext();
			}
			UpdateStatusBar();
		}

		private void UpdateStatusBar()
		{
			String searchInfo = ""; // if not searching
			if (this.searchText.Length > 0)
				searchInfo = String.Format("Search: {0} matches for {1}", MatchCount(), this.searchText);
			this.toolStripStatusSearchInfo.Text = searchInfo;
		}

		private void OnNext()
		{
			// use caret if present
			int pos = theSource.SelectionStart;
			String s = theSource.Text;
			pos = FindNext(s, pos + this.searchText.Length);
			if (pos >= 0)
			{
				theSource.SelectionStart = pos;
				theSource.SelectionLength = this.searchText.Length;
				theSource.Focus();
				theSource.ScrollToCaret();
			}
		}

		private void OnPrev()
		{
			// use caret if present
			int pos = theSource.SelectionStart;
			String s = theSource.Text;
			pos = FindPrev(s, pos);
			if (pos >= 0)
			{
				theSource.SelectionStart = pos;
				theSource.SelectionLength = this.searchText.Length;
				theSource.Focus();
				theSource.ScrollToCaret();
			}
		}

		private void BtnCopy_Click(object sender, System.EventArgs e)
		{
			theSource.SelectAll();
			theSource.Copy();
			this.Close();
		}

		private void BtnFind_Click(object sender, System.EventArgs e)
		{
			this.searchText = this.textSearch.Text;
			OnFind();
		}

		private void BtnNext_Click(object sender, System.EventArgs e)
		{
			OnNext();
		}

		private void BtnPrev_Click(object sender, System.EventArgs e)
		{
			OnPrev();
		}

		private void BtnZoom_Click(object sender, EventArgs e)
		{
			String btnTextEnlarge = "&Enlarge";
			String btnTextRestore = "Restor&e";
			Button zoom = (Button)sender;
			String buttonText = zoom.Text;
			if (buttonText == btnTextRestore) // e n l a r g e
			{
				this.SetBounds(this.holdBounds.Left, this.holdBounds.Top, this.holdBounds.Width, this.holdBounds.Height);
				zoom.Text = btnTextEnlarge;
			}
			else
			{
				int margin = 20;
				this.holdBounds = this.Bounds;
				// get screen size
				Rectangle scr = Screen.PrimaryScreen.Bounds;
				// resize window
				this.SetBounds(1, 1, scr.Width - margin, scr.Height - margin);
				zoom.Text = btnTextRestore;
			}
		}

		private String searchText;
		private Rectangle holdBounds;
	}
}
